#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/un.h>
#include "ipc_socket.h"

#define EXIT_TRUE 0
#define EXIT_FALSE 1
#define SOCKET_CLIENT 2

#define EPOLL_TIMEOUT 0
#define MAX_BUFF_LEN 1024
#define BUFF_SIZE 128


typedef struct{
    char buff[MAX_BUFF_LEN / BUFF_SIZE][BUFF_SIZE];
    unsigned int buffLen[MAX_BUFF_LEN / BUFF_SIZE];
    unsigned int buffIndex;
    unsigned int num;
    unsigned int frameIndex;
}socketBuff;

typedef struct {
    int type;
    int socketfd;
    void *next;
}socketBind;

static socketBind *fd_bind = NULL;
static socketBuff buffRecv = {0};
static int client_fd = -1;
struct sockaddr_un serveraddr;
static int exit_flag = EXIT_FALSE;

int is_exit(void)
{
    if (exit_flag == EXIT_TRUE) {
        return EXIT_TRUE;
    }
    return EXIT_FALSE;
}

static void signal_handler(int sig)
{
    exit_flag = EXIT_TRUE;
    printf("Exit:%d \n", exit_flag);
}

void quit_signal_init(void)
{
    signal(SIGINT, signal_handler);
}

int server_socket_init(void)
{
    int ret = -1;
    int fd = -1;
    int out = 1;
    // 1. 创建监听的套接字
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("server socket");
        return fd;
    }
    /* 一般在一个端口释放后需要等一段时间才能重新启用因此需要借助SO_REUSEADDR来使端口重新启用
    解决服务端异常退出之后，再次启动服务端，客户端无法使用同一个端口连接socket的问题*/
    ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &out, sizeof(out));
	if (ret < 0) {
		perror("setsockopt");
		return -1;
	}
    // 2. 将socket()返回值和本地的IP端口绑定到一起
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, SOCKET_PATH);
    ret = bind(fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(ret == -1) {
        perror("server bind");
        close(fd);
        return -1;
    }
    // 3. 设置监听
    ret = listen(fd, 128);
    if(ret == -1) {
        perror("server listen");
        close(fd);
        return -1;
    }

    return fd;
}

int client_socket_init(void)
{
    int fd = -1;
    // 1. 创建通信的套接字
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("client socket");
        return -1;
    }

    // 2. 连接服务器
    struct sockaddr_un serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, SOCKET_PATH);
    int ret = connect(fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(ret == -1)
    {
        perror("client connect");
        close(fd);
        return -1;
     }
     client_fd = fd;
     return fd;
}

int server_socket_connect(int socket_fd)
{
    int fd = -1;
    // 4. 阻塞等待并接受客户端连接
    struct sockaddr_in cliaddr;
    unsigned int clilen = sizeof(cliaddr);
    memset(&cliaddr, 0, clilen);
    fd = accept(socket_fd, (struct sockaddr*)&cliaddr, &clilen);
    if(fd == -1) {
        perror("server accept");
    }
    return fd;
}

int socket_send_msg(int socket_fd, char *buff, unsigned int len)
{
    if((buff == NULL) || (len <= 0) || (socket <= 0)){
        printf("parameters error\n");
    }

    send(socket_fd, buff, len, 0);
#ifdef DEBUG
    printf("[client_%04d] ================> %03d: %s\n", socket_fd, len, buff);
#endif
    return len;
}

int getRecvBuff(char *buff, unsigned int len)
{
    char *buffPtr = NULL;
    unsigned int index = 0;
    unsigned int buffLen = 0;
    if((buff == NULL) || (len <= 0)) {
        return 0;
    }
    if(len > BUFF_SIZE) {
#ifdef DEBUG
        printf("Error:Cache buff to short\n");
#endif
        return 0;
    }
    if (buffRecv.num <= 0) {
        return 0;
    }
    
    index = buffRecv.frameIndex;
    buffPtr = buffRecv.buff[index];
    buffLen = buffRecv.buffLen[index];
    memcpy(buff, buffPtr, buffLen);
#ifdef DEBUG
    int i = 0;
    printf("Get socket buff: index=%d data:",index);
    for (i = 0; i < buffLen; i++) {
        printf("%x ", buffPtr[i]);
    }
    printf("\n");
#endif
    memset(buffPtr, 0, BUFF_SIZE);
    buffRecv.buffLen[index] = 0;
    buffRecv.frameIndex = (index + 1) % (MAX_BUFF_LEN / BUFF_SIZE);
    buffRecv.num --;
    return buffLen;
}

int addRecvBuff(char *buff, unsigned int len)
{
    char *buffPtr = NULL;
    unsigned int index = 0;
    if((buff == NULL) || (len <= 0)){
        printf("parameters error\n");
        return 0;
    }
    if(len > BUFF_SIZE) {
        return 0;
    }
    
    index = buffRecv.buffIndex;
    buffPtr = buffRecv.buff[index];
    memcpy(buffPtr, buff, len);
    buffRecv.buffLen[index] = len;
    buffRecv.buffIndex = (index + 1) % (MAX_BUFF_LEN / BUFF_SIZE);
#ifdef DEBUG
    int i = 0;
    printf("Add socket buff: index=%d data:",index);
    for (i = 0; i < len; i++) {
        printf("%x ", buffPtr[i]);
    }
    printf("\n");
#endif
    if (buffRecv.num >= (MAX_BUFF_LEN / BUFF_SIZE - 1)) {
        index = buffRecv.frameIndex;
        buffPtr = buffRecv.buff[index];
        memset(buffPtr, 0, BUFF_SIZE);
        buffRecv.frameIndex = (index + 1) % (MAX_BUFF_LEN / BUFF_SIZE);
    } else {
        buffRecv.num ++;
    }
    return len;
}

static void *recv_handler(void *param)
{
    int ret = -1;
    int fdEp = -1;
    int eventCount = 0;
    char buff[BUFF_SIZE] = {0};
    struct epoll_event eventfd;
    eventfd.data.fd = client_fd;
    eventfd.events = EPOLLIN;
    //创建epoll
    fdEp = epoll_create(1);
    if(fdEp == -1) {
        perror("client epoll create");
        pthread_exit(&fdEp);
    }
    //将服务器socket加入监听
    ret = epoll_ctl(fdEp, EPOLL_CTL_ADD, client_fd, &eventfd);
    if (ret == -1) {
        perror("client epoll_ctl");
        pthread_exit(&ret);
    }
    //创建监听事件队列
    struct epoll_event events[SOCKET_CLIENT];
    while(is_exit()) {
        eventCount = epoll_wait(fdEp, events, SOCKET_CLIENT, EPOLL_TIMEOUT);
        if(eventCount == -1) {
            perror("client epoll_wait");
            pthread_exit(&eventCount);
        }
        if (eventCount == 0) {
            continue;
        }

        //服务器socket接收数据
        for(int i = 0; i < SOCKET_CLIENT; i++) {
            if((events[i].data.fd == client_fd) && (events[i].events & EPOLLIN)) {
                int len = recv(events[i].data.fd, buff, sizeof(buff), 0);
                if(len <= 0) {
                    perror("client_recv");
                    epoll_ctl(fdEp, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                    continue;
                }
                printf("================> [client_%04d] %03d: %s\n", events[i].data.fd, len, buff);
                addRecvBuff(buff, len);
            }
        }
    }
    ret = 0;
    printf("Exit thread\n");
    pthread_exit(&ret);
}

int socket_recv_thread(pthread_t *thread_id, int socket_fd)
{
    int ret = -1;
    int param = socket_fd;
    ret = pthread_create(thread_id, NULL, recv_handler, (void *)(&param));
    if(ret != 0) {
        perror("thread create");
        ret = -1;
    }
    //初始化退出信号处理函数
    quit_signal_init();
    return ret;
}

void socket_close(int socket_fd)
{
    close(socket_fd);
}

static int get_TLV_socketfd(int type)
{
    socketBind *nextPtr = fd_bind;
    //检查type类型范围
    if ((type <= IPC_START) || (type >= IPC_END)) {
#ifdef DEBUG
    printf("Error:The IPC type out the range\n");
#endif
        return 0;
    }

    while(nextPtr) {
        //检查是否已经存在
        if (nextPtr->type == type) {
            break;
        }
        nextPtr = nextPtr->next;
    }

    if (nextPtr == NULL) {
#ifdef DEBUG
        printf("Waring:The IPC type don't exist\n");
#endif
        return 0;
    }
#ifdef DEBUG
        printf("Get IPC socket_fd: %d\n", nextPtr->socketfd);
#endif
    return nextPtr->socketfd;
}

int socketfd_bind(int type, int socket_fd)
{
    socketBind *nextPtr = fd_bind;
    socketBind *nextlast = fd_bind;
    //检查type类型范围
    if ((type <= IPC_START) || (type >= IPC_END)) {
#ifdef DEBUG
    printf("Error:The IPC type out the range\n");
#endif
        return 0;
    }

    while(nextPtr) {
        //检查是否已经存在
        if (nextPtr->type == type) {
#ifdef DEBUG
            printf("Warning:The IPC type exist\n");
#endif
            return nextPtr->socketfd;
        }
        nextlast = nextPtr;
        nextPtr = nextPtr->next;
    }
    nextPtr = (socketBind *)malloc(sizeof(socketBind));
    memset(nextPtr, 0, sizeof(socketBind));
    nextPtr->type = type;
    nextPtr->socketfd = socket_fd;

    if(nextlast) {
        nextlast->next = nextPtr;
    } else {
        fd_bind = nextPtr;
    }
    return nextPtr->socketfd;
}

void socketfd_unbind(int type)
{
    socketBind *nextPtr = fd_bind;
    socketBind *nextPre = NULL;
    //检查type类型范围
    if ((type <= IPC_START) || (type >= IPC_END)) {
#ifdef DEBUG
    printf("Error:The IPC type out the range\n");
#endif
        return;
    }
    nextPre = nextPtr;
    while(nextPtr) {
        //检查是否已经存在
        if (nextPtr->type == type) {
            break;
        }
        nextPre = nextPtr;
        nextPtr = nextPtr->next;
    }

    if (nextPtr == NULL) {
#ifdef DEBUG
        printf("Waring:The IPC type don't exist\n");
#endif
        return;
    }
    
    if (nextPtr == fd_bind) {
        fd_bind = nextPtr->next;
    } else {
        nextPre = nextPtr->next;
    }

    free(nextPtr);
    nextPtr = NULL;
    nextPre = NULL;
}

int send_TLV_data(int type, void *data, int len)
{
    int fd = 0;
    int size = 0;
    int sendLen = 0;
    IPC_common *ipcData = NULL;

    size = sizeof(IPC_common) + len;
    ipcData = (IPC_common *)malloc(size);
    fd = get_TLV_socketfd(type);
    if (fd) {
        ipcData->type = type;
        ipcData->length = len;
        memcpy(ipcData->value, data, len);
        sendLen = socket_send_msg(fd, (char *)ipcData, size);
    }
    free(ipcData);
    return sendLen;
}

int get_TLV_data(int type, void *data, int len)
{
    char *dataPtr = NULL;
    IPC_common *ipcData = NULL;
    unsigned int buffSize = 0;
    unsigned int recvLen = 0;
        //检查type类型范围
    if ((type <= IPC_START) || (type >= IPC_END)) {
#ifdef DEBUG
        printf("Error:The IPC type out the range\n");
#endif
        return 0;
    }
    buffSize = sizeof(IPC_common) + len;
    dataPtr = (char *)malloc(buffSize);
    memset(dataPtr, 0, buffSize);

    recvLen = getRecvBuff(dataPtr, buffSize);
    ipcData = (IPC_common *)dataPtr;
    while (ipcData->type != type) {
        recvLen = getRecvBuff(dataPtr, buffSize);
        if (recvLen == 0) {
            break;
        }
        ipcData = (IPC_common *)dataPtr;
    }

    if (recvLen <= sizeof(IPC_common)) {
        free(dataPtr);
        return 0;
    }

    if ((buffSize != 0) && (len >= ipcData->length)) {
        memcpy(data, ipcData->value, ipcData->length);
    }
    free(dataPtr);
    return buffSize;
}